/**
 * 
 */
package denaris.midlet;

import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.GameCanvas;

import denaris.core.AbstractDenarisMap;
import denaris.core.Scorebar;
import denaris.core.Statusbar;

/**
 * The GameCanvas implementation for Denaris. It handles multiple Screens who
 * all share the same backbuffer.
 * 
 * @author stkl23
 * 
 */
public class DenarisGameCanvas extends GameCanvas implements Runnable {
	public static final int KEY_UP = 0;

	public static final int KEY_RIGHT = 1;

	public static final int KEY_DOWN = 2;

	public static final int KEY_LEFT = 3;

	public static final int KEY_FIRE = 4;

	public static boolean[] keyStates = new boolean[5];

	private static DenarisGameCanvas instance = null;
	
	public static int CURRENT_FRAME = 0;

	private boolean running = false;

	private Thread thread = null;

	private Graphics buffer;

	/** the current screen */
	private AbstractScreen screen;

	/** used for tim */
	private long currentTime = 0;
	/** used for timing*/
	private int fps = 0;

	/** saves the current clipping state */
	private int[] currentClipping = new int[4];

	public static class DisplayProperties {
		/** the display width */
		public static int DISPLAY_WIDTH = 200;
		/** the display height */
		public static int DISPLAY_HEIGHT = 320;
		/** the vertical start of the map */
		public static int GAME_TOP = 24;
		/** the height of the visible map part*/
		public static int GAME_HEIGHT = 320;
	
	}
	
	
	protected DenarisGameCanvas(boolean supressKeyEvents) {
		super(supressKeyEvents);
		setFullScreenMode(true);
		setBuffer(getGraphics());
		
		fillDisplayProperties();
	}
	
	private void fillDisplayProperties() {
		DisplayProperties.DISPLAY_WIDTH = this.getWidth();
		DisplayProperties.DISPLAY_HEIGHT = this.getHeight();
		DisplayProperties.GAME_TOP = 24;
		DisplayProperties.GAME_HEIGHT = 150;
	}
	
	public void adjustGameHeightToMapHeight(AbstractDenarisMap map) {
		int maxGameHeight = DisplayProperties.DISPLAY_HEIGHT - Statusbar.getInstance().getHeight() - Scorebar.getInstance().getHeight();
		int gameHeight = Math.min(maxGameHeight, map.getHeight());
		DisplayProperties.GAME_HEIGHT = gameHeight;
		DisplayProperties.GAME_TOP = (DisplayProperties.DISPLAY_HEIGHT >> 1) - (gameHeight >> 1);
	}

	/**
	 * the default paint implementation calls paintScreenToBuffer() with this
	 * screens internal backbuffer and flushes the buffers content to the
	 * display. That means that in any screens run-method calling repaint() will
	 * cause the screen to be painted and shown.
	 */
	public void paint(Graphics g) {
		// paint the screen
		screen.paintScreenToBuffer(buffer);
		// draw the FPS
		buffer.setColor(0x808080);
		buffer.drawString(fps + " fps", DisplayProperties.DISPLAY_WIDTH, DisplayProperties.GAME_TOP, Graphics.TOP | Graphics.RIGHT);

		flushGraphics();
	}

	public void showScreen(AbstractScreen screen) {
		setScreen(screen);
		// initialize the screen
		screen.initialize();
	}

	public void hold() {
		// stop the thread
		setRunning(false);
	}

	public void stop() {
		hold();
		try {
			getThread().join();
		} catch (InterruptedException e) {
			Denaris.getInstance().handleException(e);
		}
	}
	
	public void run() {
		int fps = 0;
		long temp = 0;
		while (isRunning()) {
			try {
				// update the current screen
				screen.update();
				
				// count frames per second
				fps++;
				temp = System.currentTimeMillis();
				if ((temp - currentTime) > 1000) {
					currentTime = temp;
					this.fps = fps;
					fps = 0;
				}
				// repaint the scene
				repaint();

				CURRENT_FRAME++;

				Thread.sleep(20);
			} catch (Exception e) {
				setRunning(false);
				Denaris.getInstance().handleException(e);
			}
		}
	}

	protected void keyPressed(int keyCode) {
		switch (getGameAction(keyCode)) {
		case UP: {
			keyStates[KEY_UP] = true;
			break;
		}
		case DOWN: {
			keyStates[KEY_DOWN] = true;
			break;
		}
		case RIGHT: {
			keyStates[KEY_RIGHT] = true;
			break;
		}
		case LEFT: {
			keyStates[KEY_LEFT] = true;
			break;
		}
		case FIRE: {
			keyStates[KEY_FIRE] = true;
			break;
		}
		}
	}

	protected void keyReleased(int keyCode) {
		switch (getGameAction(keyCode)) {
		case UP: {
			keyStates[KEY_UP] = false;
			break;
		}
		case DOWN: {
			keyStates[KEY_DOWN] = false;
			break;
		}
		case RIGHT: {
			keyStates[KEY_RIGHT] = false;
			break;
		}
		case LEFT: {
			keyStates[KEY_LEFT] = false;
			break;
		}
		case FIRE: {
			keyStates[KEY_FIRE] = false;
			break;
		}
		}
	}

	/**
	 * @deprecated we're using the key-events from MIDP1.0 now since Siemens
	 *             Phones cannot handle key states right
	 */
	private void updateKeyStates() {
		int states = getKeyStates();

		keyStates[KEY_UP] = (states & GameCanvas.UP_PRESSED) != 0;
		keyStates[KEY_DOWN] = (states & GameCanvas.DOWN_PRESSED) != 0;
		keyStates[KEY_RIGHT] = (states & GameCanvas.RIGHT_PRESSED) != 0;
		keyStates[KEY_LEFT] = (states & GameCanvas.LEFT_PRESSED) != 0;
		keyStates[KEY_FIRE] = (states & GameCanvas.FIRE_PRESSED) != 0;
	}

	public boolean isRunning() {
		return running;
	}

	public void setRunning(boolean running) {
		this.running = running;
	}

	public Thread getThread() {
		return thread;
	}

	public Graphics getBuffer() {
		return buffer;
	}

	public void setBuffer(Graphics buffer) {
		this.buffer = buffer;
	}

	public AbstractScreen getScreen() {
		return screen;
	}

	public void setScreen(AbstractScreen screen) {
		this.screen = screen;
	}

	public void saveCurrentClipping() {
		Graphics buffer = getBuffer();
		currentClipping[0] = buffer.getClipX();
		currentClipping[1] = buffer.getClipY();
		currentClipping[2] = buffer.getClipWidth();
		currentClipping[3] = buffer.getClipHeight();
	}

	public void restoreClipping() {
		getBuffer().setClip(currentClipping[0], currentClipping[1],	currentClipping[2], currentClipping[3]);
	}
	
	public static DenarisGameCanvas getInstance() {
		if (instance == null) {
			instance = new DenarisGameCanvas(false);
		}
		
		return instance;
	}

	public void show() {
		// bring up the canvas
		Display.getDisplay(Denaris.getInstance()).setCurrent(this);
		// start the thread
		setRunning(true);
		thread = new Thread(this);
		thread.start();
	}
}
